package main

// #cgo CFLAGS: -g -Wall -I../PureFlash/common/include -I.
// #include <stdint.h>
// #include <stdlib.h>
// #include <string.h>
// #include "pfk_client_api.h"
import "C"
import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"time"
	"unsafe"

	"github.com/go-zookeeper/zk"
	"gopkg.in/ini.v1"
)

// PfClientShardInfo represents the C++ struct PfClientShardInfo
type PfClientShardInfo struct {
	Index          int      `json:"index"`
	StoreIPs       string   `json:"store_ips"` // a comma separated string from jconductor
	Status         string   `json:"status"`
	ParsedStoreIps []string `json:"parsed_store_ips"` // splitted from store_ips
}

// ParseStoreIPs splits the StoreIPs string into a slice of strings
func (p *PfClientShardInfo) ParseStoreIPs() {
	p.ParsedStoreIps = strings.Split(p.StoreIPs, ",")
}

type PfVolumeState int

const (
	// VOLUME_CLOSED represents the volume is closed
	VOLUME_CLOSED PfVolumeState = 0
	// VOLUME_OPENED represents the volume is opened
	VOLUME_OPENED PfVolumeState = 1
	// VOLUME_WAIT_REOPEN represents the volume is waiting to reopen
	VOLUME_WAIT_REOPEN PfVolumeState = 2
	// VOLUME_REOPEN_FAIL represents the volume reopen failed
	VOLUME_REOPEN_FAIL PfVolumeState = 3
	// VOLUME_DISCONNECTED represents the volume is disconnected
	VOLUME_DISCONNECTED PfVolumeState = 4
)

// String returns the string representation of the PfVolumeState
func (s PfVolumeState) String() string {
	switch s {
	case VOLUME_CLOSED:
		return "VOLUME_CLOSED"
	case VOLUME_OPENED:
		return "VOLUME_OPENED"
	case VOLUME_WAIT_REOPEN:
		return "VOLUME_WAIT_REOPEN"
	case VOLUME_REOPEN_FAIL:
		return "VOLUME_REOPEN_FAIL"
	case VOLUME_DISCONNECTED:
		return "VOLUME_DISCONNECTED"
	default:
		return "UNKNOWN"
	}
}

type PfClientVolume struct {
	//following data are from server open_volume reply
	Status     string `json:"status"`
	VolumeName string `json:"volume_name,omitempty"`

	VolumeSize int64               `json:"volume_size"`
	VolumeId   int64               `json:"volume_id"`
	ShardCount int                 `json:"shard_count"`
	RepCount   int                 `json:"rep_count"`
	MetaVer    int                 `json:"meta_ver"`
	SnapSeq    uint32              `json:"snap_seq"`
	Shards     []PfClientShardInfo `json:"shards"`

	ShardLbaCntOrder int
}

const MAX_IP_PER_SHARD = 6

func main() {
	var detachVolume string
	var attachVolume string
	var listVolume bool
	var configFile string
	var snapshotName string

	flag.StringVar(&detachVolume, "d", "", "Detach volume")
	flag.StringVar(&attachVolume, "a", "", "Attach volume")
	flag.BoolVar(&listVolume, "l", false, "List all volumes")
	flag.StringVar(&snapshotName, "s", "", "Snapshot name (use with attach)")
	flag.StringVar(&configFile, "c", "/etc/pureflash/pf.conf", "Config file")
	// 解析命令行参数
	flag.Parse()

	_ = listVolume
	_ = detachVolume

	if attachVolume != "" && detachVolume == "" {
		fmt.Println("Attach volume: " + attachVolume)
		doAttachVolume(attachVolume, snapshotName, configFile)
	} else if detachVolume != "" {
		fmt.Println("Detach volume: " + detachVolume)
		doDetachVolume(detachVolume)
	} else if listVolume {
		fmt.Println("List volumes")
		doListVolumes()
	} else {
		flag.Usage()
		os.Exit(1)
	}
}

func doAttachVolume(volumeName, snapshotName string, configFile string) {

	cfg, err := ini.Load(configFile)
	if err != nil {
		fmt.Printf("Fail to read file: %v", err)
		panic(err)
	}
	clusterName := cfg.Section("cluster").Key("name").String()
	zkIp := cfg.Section("zookeeper").Key("ip").String()
	zkIps := strings.Split(zkIp, ",")

	// 连接到ZooKeeper
	conn, _, err := zk.Connect(zkIps, 10*time.Second)
	if err != nil {
		fmt.Printf("Failed to connect to ZooKeeper: %v\n", err)
		panic(err)
	}
	defer conn.Close()

	// 获取/pureflahs/cluster0/conductors路径下的所有子节点
	children, _, err := conn.Children("/pureflash/" + clusterName + "/conductors")
	if err != nil {
		fmt.Printf("Failed to get children of /pureflahs/%s/conductors: %v\n", clusterName, err)
		panic(err)
	}

	// 对子节点名称进行排序
	sort.Strings(children)

	conductorIp := ""
	// 如果有子节点，获取排序后第一个节点的内容
	if len(children) > 0 {
		firstChild := children[0]
		data, _, err := conn.Get("/pureflash/" + clusterName + "/conductors/" + firstChild)
		if err != nil {
			panic(err)
		}

		// 打印第一个节点的名称和内容
		fmt.Printf("First child node: %s\n", firstChild)
		fmt.Printf("Content of the first child node: %s\n", data)
		conductorIp = string(data)
	} else {
		fmt.Println("No child nodes found.")
	}

	volumeInfo := PfClientVolume{}

	url := fmt.Sprintf("http://%s:49180/s5c/?op=open_volume&volume_name=%s&snapshot_name=%s",
		conductorIp, url.QueryEscape(volumeName), url.QueryEscape(snapshotName))
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		panic(err)
	} else {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("read body error")
			panic(err)
		}
		//fmt.Printf("%s\n", string(body))
		json.Unmarshal([]byte(body), &volumeInfo)
	}
	kvol := (*C.struct_PfkClientVolumeInfo)(C.calloc(1, 4096))
	kvol.struct_ver = 1
	//safe_strcpy(kvol->volume_name, vol->volume_name.c_str(), sizeof(kvol->volume_name));
	//cpyGoToC(&kvol.volume_name[0], volumeInfo.VolumeName)
	cpyGoToC(&kvol.volume_name[0], volumeInfo.VolumeName)
	//safe_strcpy(kvol->status, vol->status.c_str(), sizeof(kvol->status));
	cpyGoToC(&kvol.status[0], volumeInfo.Status)
	//safe_strcpy(kvol->snap_name, vol->snap_name.c_str(), sizeof(kvol->snap_name));
	cpyGoToC(&kvol.snap_name[0], snapshotName)
	// kvol->volume_size = vol->volume_size;
	kvol.volume_size = C.uint64_t(volumeInfo.VolumeSize)
	// kvol->volume_id = vol->volume_id;
	kvol.volume_id = C.uint64_t(volumeInfo.VolumeId)
	// kvol->shard_count = vol->shard_count;
	kvol.shard_count = C.int(volumeInfo.ShardCount)
	// kvol->rep_count = vol->rep_count;
	kvol.rep_count = C.int(volumeInfo.RepCount)
	// kvol->meta_ver = vol->meta_ver;
	kvol.meta_ver = C.int(volumeInfo.MetaVer)
	// kvol->snap_seq = vol->snap_seq;
	kvol.snap_seq = C.int(volumeInfo.SnapSeq)

	// for(int i=0;i<(int)vol->shards.size();i++){
	// 	//const PfClientShardInfo& shard = shards[i];
	// 	kvol->shards[i].index = vol->shards[i].index;
	// 	for(int j=0;j<vol->shards[i].parsed_store_ips.size();j++){
	// 		if(j>=S5ARRAY_SIZE(kvol->shards[i].parsed_store_ips)){
	// 			S5LOG_WARN("Too many store IP, ignore it:%s", vol->shards[i].parsed_store_ips[j].c_str());
	// 			continue;
	// 		}
	// 		char ip[32];
	// 		safe_strcpy(ip, vol->shards[i].parsed_store_ips[j].c_str(), sizeof(ip));
	// 		int ipv4 = 0;

	// 		for (char* p = strtok(ip, "."); p != NULL; p = strtok(NULL, ".")) {
	// 			ipv4 = (ipv4<<8)|atoi(p);
	// 		}
	// 		kvol->shards[i].parsed_store_ips[j].ip_v4 = ipv4;
	// 	}
	// }
	//convertShards(&volumeInfo.Shards, (*kvol).shards)

	for i := 0; i < len(volumeInfo.Shards); i++ {

		volShard := &volumeInfo.Shards[i]
		volShard.ParseStoreIPs()
		kvol.shards[i].index = C.int(volShard.Index)

		for j := 0; j < len(volShard.ParsedStoreIps); j++ {
			if j >= MAX_IP_PER_SHARD {
				fmt.Printf("Too many store IP, ignore it: %s\n", volShard.ParsedStoreIps[j])
				break
			}
			ipV4, err := convertIPtoV4(volShard.ParsedStoreIps[j])
			if err != nil {
				fmt.Printf("Error converting IP: %s\n", err)
				continue
			}
			kvol.shards[i].parsed_store_ips[j].ip_v4 = C.uint32_t(ipV4)
		}
	}

	fd, err := syscall.Open("/sys/pureflash/open", syscall.O_WRONLY, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't open /sys/pureflash/open: %v\n", err)
		return
	}
	defer syscall.Close(fd) // 确保在函数返回前关闭文件描述符
	buf_size := C.sizeof_struct_PfkClientVolumeInfo + C.sizeof_struct_PfkClientShardInfo*(volumeInfo.ShardCount-1)
	ptr := unsafe.Pointer(kvol)
	byteSlice := (*[]byte)(unsafe.Pointer(&ptr))
	rs, err := syscall.Write(fd, (*byteSlice)[:buf_size])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write to /sys/pureflash/open: %v\n", err)
		return
	}

	if rs == buf_size {
		fmt.Printf("Succeeded opening volume\n")
	} else {
		fmt.Println("Failed to write /sys/pureflash/open")
		os.Exit(1)
	}

}

// 这个函数假设你已经有了一个转换IP地址的辅助函数
func convertIPtoV4(ip string) (uint32, error) {
	parts := strings.Split(ip, ".")
	if len(parts) != 4 {
		return 0, fmt.Errorf("invalid IP address: %s", ip)
	}
	ipv4 := uint32(0)
	for _, part := range parts {
		num, err := strconv.Atoi(part)
		if err != nil {
			return 0, err
		}
		ipv4 = (ipv4 << 8) | uint32(num)
	}
	return ipv4, nil
}

func cpyGoToC(dst *C.char, src string) {
	// run : GODEBUG=cgocheck=0 ./gohelper -a test1
	//C.memcpy(unsafe.Pointer(dst), (unsafe.Pointer(&src)), C.ulong(len(src)))
	buf := []byte(src)
	dstBuf := (*[1 << 30]C.char)(unsafe.Pointer(dst))
	for i := 0; i < len(buf); i++ {
		dstBuf[i] = C.char(buf[i])
	}
	dstBuf[len(buf)] = 0

}

func doDetachVolume(volumeName string) {

	// 1. 打开 /sys/pureflash/close
	// 2. 写入 volumeName
	// 3. 关闭 /sys/pureflash/close
	fd, err := syscall.Open("/sys/pureflash/close", syscall.O_WRONLY, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't open /sys/pureflash/close: %v\n", err)
		return
	}
	defer syscall.Close(fd) // 确保在函数返回前关闭文件描述符
	// 写入 volumeName
	buf := make([]byte, 0, 100)
	buf = append(buf, volumeName...)
	buf = append(buf, 0)
	rs, err := syscall.Write(fd, buf)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write to /sys/pureflash/close: %v\n", err)
		return
	}
	if rs == len(buf) {
		fmt.Printf("Succeeded detaching volume %s\n", volumeName)
	} else {
		fmt.Println("Failed to write /sys/pureflash/close")
		os.Exit(1)
	}
	return
}

func doListVolumes() {
	// 1. 打开 /sys/pureflash/list
	fd, err := syscall.Open("/sys/pureflash/list", syscall.O_RDONLY, 0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't open /sys/pureflash/list: %v\n", err)
		return
	}
	defer syscall.Close(fd) // 确保在函数返回前关闭文件描述符
	buf := make([]byte, 4096)
	rs, err := syscall.Read(fd, buf)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read /sys/pureflash/list: %v\n", err)
	} else {
		fmt.Printf("%s\n", buf[:rs])
	}
	return
}
